//想实现platform + irq + key + fasync
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/interrupt.h>


#define API_ENTER() printk("func: %s at line: %d is called\r\n", __FUNCTION__, __LINE__);
#define API_EXIT()  printk("func: %s exit\r\n", __FUNCTION__);
#define DEVICE_COUNT 1

static int major = 0;
static int minor = 0;
static struct cdev cdev;
static struct class * pClass = NULL;
static struct resource *g_res = NULL;
static struct resource *g_les = NULL;
static struct fasync_struct *s_fasync;
static wait_queue_head_t readQueue;
static int canRead = 0;
static int keyValue = 0;

//中断服务程序
irqreturn_t interruptHandle(int irq, void * dev_id)
{
	API_ENTER();
	canRead=1;
	printk("interruptHandle irq=%d\r\n",irq);
	keyValue=irq;	
	//wake_up_interruptible(&readQueue);
	//发送信号给应用程序
	printk("k %d pressed\r\n", irq);
	kill_fasync(&s_fasync,SIGIO,POLL_IN);
	//if(irq==g_res->start)
	API_EXIT();	
	return IRQ_HANDLED;
}

int hello_open(struct inode * inode , struct file * file)
{
	//区分设备
	dev_t devNo;
	int openMinor=0;
	int ret=0;
	devNo = inode->i_rdev;
	openMinor=MINOR(devNo);
	printk("file is open, minor = %d\r\n", openMinor);
	//申请中断
	ret = request_irq(g_res->start, interruptHandle, g_res->flags, "fs4412-key", NULL);
	if(ret)
	{	
		printk("request error\r\n");
		return ret;
	}
	ret = request_irq(g_les->start, interruptHandle, g_les->flags, "fs4412-key1", NULL);
	if(ret)
	{	
		printk("request error\r\n");
		return ret;
	}
	API_EXIT();
	return 0;
}

//释放中断
int hello_close(struct inode * inode , struct file * file)
{
	//释放中断
	API_ENTER();
	free_irq(g_res->start, NULL);
	free_irq(g_les->start, NULL);	
	API_EXIT();
	return 0;
}

ssize_t hello_read(struct file * filp, char __user * uBuf, size_t length, loff_t * offset)
{
	//如果没有数据，并且阻塞打开，则阻塞
	//如果没有数据，并且非阻塞打开，则立即返回
	if (!canRead)//没有数据
	{
		if (O_NONBLOCK & filp->f_flags)//非阻塞打开
		{
			return -EAGAIN;
		}
		//阻塞打开
		wait_event_interruptible(readQueue, canRead);
	}
	//如果有数据，copy_to_user
	if (copy_to_user(uBuf, &keyValue, length))
	{
		printk("copy to user error\r\n");
		return -EFAULT;
	}	
	canRead = 0;
	API_EXIT();
	return length;
}

static int hello_fasync(int fd, struct file *file, int on)
{
	return fasync_helper(fd, file, on, &s_fasync);
}

static struct file_operations fops = {
	.owner  = THIS_MODULE,
	.open   = hello_open,
	.read   = hello_read,
	.release = hello_close,
	.fasync = hello_fasync,
};

static int hello_probe(struct platform_device * pdev)
{
	dev_t devNo = 0;
	int ret = 0;
	int i = 0;
	struct device * pDevice = NULL;
	API_ENTER();
	//申请设备号
	devNo = MKDEV(major, minor);
	if (major)
	{
		ret = register_chrdev_region(devNo, DEVICE_COUNT, "key_dev");
	}
	else 
	{
		ret = alloc_chrdev_region(&devNo, minor, DEVICE_COUNT, "key_dev");
	}
	
	if (ret)
	{
		printk("register char devNo error\r\n");
		goto REGISTER_ERR;
	}
	major = MAJOR(devNo);
	minor = MINOR(devNo);
	printk("register success, major = %d, minor = %d\r\n", major, minor);
	//初始化cdev -- 
	cdev_init(&cdev, &fops);
	cdev.owner = THIS_MODULE;
	//加载cdev
	ret = cdev_add(&cdev, devNo, DEVICE_COUNT);
	if (ret)
	{
		printk("cdev add error\r\n");
		goto CDEV_ADD_ERR;
	}
	//创建class
	pClass = class_create(THIS_MODULE, "key_devClass");
	if (IS_ERR(pClass))
	{
		printk("class create error\r\n");
		ret = -EFAULT;
		goto CLASS_CREATE_ERR;
	}
	//创建设备名
	for (i = 0; i < 1; i++)
	{
		pDevice = device_create(pClass, NULL, MKDEV(major, minor + i), NULL, "key_dev%d", i);
		if (IS_ERR(pDevice)) 
		{
			printk("device create error\r\n");
			ret = -EFAULT;
			goto DEVICE_CREATE_ERR;
		}	
	}		
	
	//获取资源
	g_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (NULL == g_res)
	{
		ret = -ENXIO;
		printk("get resource error-------\r\n");
		goto GET_RESOURCE_ERR;
	}
	g_les = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
	if (NULL == g_les)
	{
		ret = -ENXIO;
		printk("get resource error-------\r\n");
		goto GET_RESOURCE_ERR;
	}
	printk("get res ok , start=%d\r\n", g_res->start);
	printk("get les ok , start=%d\r\n", g_les->start);
	//物理地址映射虚拟地址
	//初始化硬件地址 config/data寄存器赋值
	API_EXIT();
	return 0;
GET_RESOURCE_ERR:
	//device销毁
	for (i = 0; i < DEVICE_COUNT; i++)
	{
		device_destroy(pClass, MKDEV(major, minor + i));
	}
DEVICE_CREATE_ERR:
	class_destroy(pClass);
CLASS_CREATE_ERR:
	cdev_del(&cdev);
CDEV_ADD_ERR:
	unregister_chrdev_region(devNo, DEVICE_COUNT);	
REGISTER_ERR:
	return ret;
}

static int hello_remove(struct platform_device *pdev)
{
	int i = 0;
	dev_t devNo;
	//解除--物理地址映射虚拟地址
	//删除设备名
	for (i = 0; i < DEVICE_COUNT; i++)
	{
		device_destroy(pClass, MKDEV(major, minor + i));
	}
	//删除class
	class_destroy(pClass);
	//删除cdev
	cdev_del(&cdev);
	//删除设备号
	devNo = MKDEV(major, minor);
	unregister_chrdev_region(devNo, DEVICE_COUNT);
	return 0;
}

//匹配设备树中的key节点
static const struct of_device_id hello_match_table[] = {
	{ .compatible = "fs4412,key" ,},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hello_match_table);

//定义一个platform_driver变量
static struct platform_driver hello_driver = {
	.probe      = hello_probe,
	.remove		= hello_remove,
	.driver     = {
		.name   = "helloPlatform",
		.of_match_table = hello_match_table,
	},
};

static int __init hello_platform_init(void)
{
	//注册一个platform_driver
	API_ENTER();
	platform_driver_register(&hello_driver);	
	API_EXIT();
	return 0;
}

static void __exit hello_platform_exit(void)
{
	//注销一个platform_driver
	API_ENTER();
	platform_driver_unregister(&hello_driver);	
	API_EXIT();
}


module_init(hello_platform_init);
module_exit(hello_platform_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("platform key fasync for FS4412");
MODULE_AUTHOR("<R>");
MODULE_VERSION("1.0");
